/*
	init_AP_64.c

	Contains functionality for initialising the system's Application processors.

	Author: Aidan Goddard 30/6/13
*/

#include "../kernel/headers/types.h"
#include "../kernel/headers/system_config_tables.h"
#include"headers/ISR.h"
#include"headers/printf.h"

/*
	Main function to initialise all available application processors (APs)
	in the system.

	No arguments.

	Returns when all APs are sucessfully initialised.

	NOTE: APIC timer must be previously configured to fire every 10ms (periodic).
*/
/*
uint32_t InitAPs()
{
	// copy trampoline code (4096) from 0x10000 to 0x2000
	uint8_t *ptr1 = (uint8_t*)(0x10000 + KOFFSET);
	uint8_t *ptr2 = (uint8_t*)( 0x2000 + KOFFSET);
	for(int i = 0; i < 4096; i++)
	{
		ptr2[i] = ptr1[i];
	}

	// set the APIC timer ISR to the dummy one
	//DisableISR(50);
	//RegisterISR(50, (uint64_t)&IRQ_dummy_APIC, 0x08, CPL_0, GATE_INTERRUPT, IST_1);

	// get the sys info table ptr
	TBL_SYSCONFIG *config_base = GetSystemConfigBase();

	// get APIC base address
	uint32_t apic_low, apic_high;
	rdmsr(0x1b, &apic_low, &apic_high);		// get msr contents
	volatile uint32_t *APIC_base = (uint32_t*)(uint64_t)(apic_low & 0xfffff000);

	// for every CPU but the BSP
	for(int i = 1; i < 32; i++)
	{
		// check it is enabled
		if (config_base->tbl_cpu[i].enabled == 0)
		{
			// last entry, leave
			break;
		}

		// get the APIC ID
		uint32_t APIC_ID = config_base->tbl_cpu[i].APIC_ID;
		printf("\n[AP INIT] Initialising CPU %u APIC ID 0x%x", i, APIC_ID);
		APIC_ID = APIC_ID << 24;

		// set IPI address register to the APIC ID
		__asm__ __volatile__("":::"memory");
		*(APIC_base + (0x310 / 4)) = APIC_ID;
		__asm__ __volatile__("":::"memory");

		// send the INIT IPI and wait
		uint32_t INIT_msg = 0x00004500;
		__asm__ __volatile__("":::"memory");
		*(APIC_base + (0x300 / 4)) = INIT_msg;
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti; hlt; hlt; cli;");

		// send first SIPI IPI and wait
		uint32_t SIPI_msg = 0x00004602;
		__asm__ __volatile__("":::"memory");
		*(APIC_base + (0x300 / 4)) = SIPI_msg;
		__asm__ __volatile__("":::"memory");
		printf("\n[AP INIT] Awaiting check signal");
		__asm__ __volatile__("sti; hlt; hlt; cli;");

		// check control variable at (0x10100 - 4) for 1
		volatile uint32_t *check = (uint32_t*)(0x2100 - 4 + KOFFSET);
		volatile uint32_t *ready = (uint32_t*)(0x2100 - 12 + KOFFSET);
		if(*check == 1)
		{
			// AP has initialised correctly, move to next one when it's ready
			*check = 0;
			printf("\n[AP INIT] Awaiting ready signal");
			while(*ready == 0)
			{
				//__asm__ __volatile__("sti; hlt; cli;");
			}
			*ready = 0;
			printf("\n[AP INIT] Initialised  CPU %u APIC ID 0x%x", i, APIC_ID >> 24);
			continue;
		}

		// otherwise send the second SIPI IPI and wait
		__asm__ __volatile__("":::"memory");
		*(APIC_base + (0x300 / 4)) = SIPI_msg;
		__asm__ __volatile__("":::"memory");
		printf("\n[AP INIT] Awaiting check signal");
		__asm__ __volatile__("sti; hlt; hlt; cli;");

		// check control variable again
		if(*check == 1)
		{
			// AP has initialised correctly, move to next one when it's ready
			*check = 0;
			printf("\n[AP INIT] Awaiting ready signal");
			while(*ready == 0)
			{
				//__asm__ __volatile__("sti; hlt; cli;");
			}
			*ready = 0;
			printf("\n[AP INIT] (2nd attempt) Initialised  CPU %u APIC ID 0x%x", i, APIC_ID);
			continue;
		}

		// AP has not initialised correctly
		printf("\n[AP INIT] ERROR: could not initialise CPU %u", i);
		config_base->tbl_cpu[i].enabled = 2;		// set it to 'startup failed' in the system table

	}

	// get initialised count
	volatile uint32_t *count = (uint32_t*)(0x2100 - 8 + KOFFSET);
	printf("\n[AP INIT] Total CPUs in operation: %u", *count);

	__asm__ __volatile__ ("cli");
	return *count;
}
*/
